package com.cdy.server.handler;

import com.cdy.common.command.Command;
import com.cdy.common.command.RequestUploadCommand;
import com.cdy.common.domain.FileEntity;
import com.cdy.common.domain.FileSectionEntity;
import com.cdy.common.property.CommonProperty;
import com.cdy.common.response.RequestUploadResponse;
import com.cdy.store.repository.FileRepository;
import io.netty.channel.ChannelHandlerContext;

import java.util.BitSet;
import java.util.List;

/**
 * 请求命令处理
 * <p>
 * Created by 陈东一
 * 2019/10/20 0020 12:23
 */
public class RequestUploadCommandHandler implements CommandHandler {

    private final FileRepository fileRepository;

    public RequestUploadCommandHandler(FileRepository fileRepository) {
        this.fileRepository = fileRepository;
    }

    @Override
    public void handle(ChannelHandlerContext ctx, Command msg) {
        RequestUploadCommand requestCommand = (RequestUploadCommand) msg;
        FileEntity fileEntity = fileRepository.getFileByFileMd5(requestCommand.getFileMD5());
        RequestUploadResponse requestResponse = new RequestUploadResponse();
        requestResponse.setRequestId(requestCommand.getRequestId());
        requestResponse.setComplete(false);
        // 初始化文件对象
        if (fileEntity == null) {
            fileEntity = new FileEntity();
            fileEntity.setComplete(false);
            fileEntity.setMd5(requestCommand.getFileMD5());
            fileEntity.setName(requestCommand.getFileName());
            fileEntity.setLength(requestCommand.getFileLength());
            fileRepository.insertFile(fileEntity);
        }

        if (!fileEntity.getComplete()) { //文件未上传完成
            List<FileSectionEntity> fileSectionEntities;
            synchronized (fileRepository) {
                fileSectionEntities = fileRepository.listTaskByFileId(fileEntity.getId());
                if (fileSectionEntities == null) { //不存在分片
                    fileSectionEntities = fileEntity.createSectionTasks(requestCommand.getClientId());
                    fileRepository.insertFileSectionList(fileEntity.getId(), fileSectionEntities);
                }
            }
            List<FileSectionEntity> finalFileSectionEntities = fileSectionEntities;
            FileSectionEntity fileSectionEntity = fileSectionEntities.stream()
                    .filter(e -> e.getClientId().equals(requestCommand.getClientId())) //优先找有自己clientId(分段未完成的)
                    .filter(e -> !e.getComplete())
                    .findFirst()
                    .orElseGet(() -> finalFileSectionEntities.stream()
                            .filter(e -> e.getClientId() == null) //其次再分配没有clientId的
                            .filter(e -> !e.getComplete())
                            .findFirst().orElse(null));

            if (fileSectionEntity != null) { //分片存在未分配的
                fileSectionEntity.setClientId(requestCommand.getClientId());
                fileRepository.updateTaskById(fileSectionEntity); //分配client
                requestResponse.setSectionId(fileSectionEntity.getId());
                BitSet bitSet = fileSectionEntity.getBitSet();
                int begin;
                if ((begin = bitSet.nextClearBit(0)) == fileSectionEntity.getSectionSize()) {
                    // 一般不会走这一步, 除非存在 complete和bitset 不一致
                    requestResponse.setComplete(true);
                    write(ctx, requestResponse);
                    return;
                }
                //设置客户端最开始的开始位置
                requestResponse.setBegin(fileSectionEntity.getBegin()+begin * CommonProperty.SmallSectionLength);
                requestResponse.setBitBegin(begin);
                int end = bitSet.nextSetBit(begin);
                end = (end == -1) ? fileSectionEntity.getSectionSize() : end;
                requestResponse.setBitSize(end-begin);
                requestResponse.setBitTotal(fileSectionEntity.getSectionSize());
                requestResponse.setSectionLength(fileSectionEntity.getLength());
                requestResponse.setSectionBegin(fileSectionEntity.getBegin());
                write(ctx, requestResponse);
                return;
            }
        }
        // 已经上传完成 或者 找不到未分配的分片信息(多客户端上传,没有空余的分段分配)
        requestResponse.setComplete(true);
        write(ctx, requestResponse);

    }
}
